05 - 采样与数据管道
前置:04 篇的内容采集档位 —— 记不记 prompt 直接决定本篇所有数字。
本篇回答:这些数据一天几个 T,怎么少存又不丢关键信息。以及为什么传统 APM 那套采样配置搬到 Agent 上会失效。
本篇会用到的词:
| 词 | 意思 |
|---|---|
| 头采样(head sampling) | 在 trace 刚开始时就决定采不采。快、省内存,但那时你还不知道这次执行会不会出错 |
| 尾采样(tail sampling) | 等一棵 trace 的 span 收得差不多了再决定。能按结果筛选,代价是要把 span 攒在内存里 |
| Collector | OpenTelemetry 的数据管道进程。收 → 处理 → 转发,是做采样、脱敏、归一的地方 |
| OTLP | OpenTelemetry 的传输协议。SDK 发给 Collector、Collector 发给后端,走的都是它 |
| 基数(cardinality) | 一个属性有多少种不同取值。user_id 基数百万,model 基数几十 —— 高基数属性做成指标标签会把时序库打爆 |
| span metrics | 从 span 里现算出来的指标。它在采样之前算,所以采样丢掉的 trace 仍然计入统计 |
一、Agent trace 有三个反常特征
先把体积算清楚,因为后面所有决策都建立在这个数字上。
1.1 一天到底多少数据
假设:日均 1,000 万次 Agent 执行,每次平均 8 个 span,其中 3 个是模型调用。
| 内容采集档位(04 篇) | 单 span 均值 | 每次执行 | 日增量 | 30 天留存 |
|---|---|---|---|---|
| 档位 1:不记 prompt | 0.6 KB | 4.8 KB | 48 GB | 1.4 TB |
| 档位 2:记在 span 属性上 | 8 KB | 64 KB | 640 GB | 19 TB |
| 档位 2 + 长上下文(32K prompt) | 45 KB | 360 KB | 3.6 TB | 108 TB |
(按 1 token ≈ 3 字节 UTF-8 中文估算;实际值随业务差异很大,重点是量级不是精度)
记不记 prompt,差 13 倍;长上下文场景差 75 倍。 这就是 04 篇第三档"存到外部存储、span 上只留引用"的经济动机 —— 对象存储的单位成本比 OLAP 数据库低一到两个数量级,而 prompt 是"偶尔要看一眼、从不用来做聚合查询"的典型冷数据。
二、头采样在 Agent 上没什么用
传统做法是在 SDK 侧配一个比例:
# ❌ 这个配置在 Agent 场景下几乎等于关掉了排查能力
# 问题在于「决定采不采」发生在根 span 创建的那一刻,
# 而那时这次执行有没有出错、绕了几步、花了多少钱,全都还不知道
sampler = TraceIdRatioBased(0.1) # 只留 10%
传统服务里这么做还行,因为请求高度同质 —— 随机留 10% 得到的是一个有代表性的样本。Agent 不是:
| 特征 | 后果 |
|---|---|
| 失败率低但失败很贵 | 1% 的失败请求里,10% 采样后只剩 0.1%,样本量不够定位 |
| 长尾极重 | 你要查的永远是那条 90 秒的、绕了 40 步的执行,而它被随机丢掉的概率和别的一样 |
| 成本分布极不均匀 | 少数请求消耗大部分 token,采样后的成本统计与真实账单对不上 |
头采样唯一还成立的用法,是给 Collector 做限流兜底 —— 防止某个服务突然发疯把管道打爆,而不是作为常规策略。
三、尾采样的三个前提,Agent 打破了两个
尾采样处理器(tailsamplingprocessor,beta 阶段)的做法是把 span 攒在内存里,等一段时间后按策略判断。它有三个隐含前提。
3.1 前提一:trace 在 decision_wait 内结束
processors:
tail_sampling:
decision_wait: 30s # ← 默认值
num_traces: 50000 # ← 默认值,内存里最多攒这么多棵树